/*
 * Copyright 2011 group coding kata nantes
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package fr.coding.kata.nantes.fizzbuzz.sylvain;

import fr.coding.kata.nantes.fizzbuzz.commons.FizzBuzzInterface;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class implement the FizzBuzz test.
 * <p>
 * Write a program that prints the numbers from 1 to 100. But for multiples of three print 'Fizz'
 * instead of the number and for the multiples of five print 'Buzz'. For numbers which are multiples
 * of both three and five print 'FizzBuzz'.
 * </p>
 *
 * @author Sylvain Leduby
 */
public class FizzBuzzGame implements FizzBuzzInterface {

    private static final Logger LOG = LoggerFactory.getLogger(FizzBuzzGame.class);

    /**
     * Default number of values to process.
     */
    private static final Integer DEFAULT_UPPER_LIMIT = 100;

    /**
     * Default constructor.
     */
    public FizzBuzzGame() {
        super();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String call() {
        return printFizzBuzz(DEFAULT_UPPER_LIMIT);
    }

    /**
     * This method generates the FizzBuzz suite for numbers from 1 to {@code nbElement}.
     *
     * @param nbElement The upper limit of the suite.
     */
    public String printFizzBuzz(final int nbElement) {
        LOG.debug("Method start: printFizzBuzz({})", nbElement);

        final StringBuilder buffer = new StringBuilder(507);
        int i = 0;
        // computes FizzBuzz
        while (i++ < nbElement) {
            toFizzBuzz(i, buffer).append('\n');
        }

        LOG.debug("Method start: printFizzBuzz");

        return buffer.toString();
    }

    /**
     * If {@code number} is a multiple of three this method returns 'Fizz' instead of the
     * {@code number} and for the multiples of five it returns 'Buzz'. For numbers which are
     * multiples of both three and five returns 'FizzBuzz'. If {@code number} is not a multiple of
     * three nor five, this method returns {@code number}.
     *
     * @param number The value.
     * @return <ul>
     *         <li>'Fizz' if {@code number} a multiple of three</li>
     *         <li>'Buzz' if {@code number} a multiple of five</li>
     *         <li>'FizzBuzz' if {@code number} a multiple of three and five</li>
     *         <li>{@code number} either</li>
     *         </ul>
     */
    public String toFizzBuzz(final int number) {
        return toFizzBuzz(number, new StringBuilder()).toString();
    }

    /**
     * If {@code number} is a multiple of three this method returns 'Fizz' instead of the
     * {@code number} and for the multiples of five it returns 'Buzz'. For numbers which are
     * multiples of both three and five returns 'FizzBuzz'. If {@code number} is not a multiple of
     * three nor five, this method returns {@code number}.
     *
     * @param number   The value.
     * @param appendTo The buffer to append to.
     * @return <ul>
     *         <li>'Fizz' if {@code number} a multiple of three</li>
     *         <li>'Buzz' if {@code number} a multiple of five</li>
     *         <li>'FizzBuzz' if {@code number} a multiple of three and five</li>
     *         <li>{@code number} either</li>
     *         </ul>
     */
    public StringBuilder toFizzBuzz(final int number, final StringBuilder appendTo) {
        final boolean multipleOf3 = isMultipleOf(number, 3);
        if (multipleOf3) {
            appendTo.append("Fizz");
        }

        final boolean multipleOf5 = isMultipleOf(number, 5);
        if (multipleOf5) {
            appendTo.append("Buzz");
        }

        if (!multipleOf3 && !multipleOf5) {
            appendTo.append(Integer.toString(number));
        }

        return appendTo;
    }

    /**
     * This method determines if {@code number} is a multiple of {@code divider}.
     *
     * @param number  The number.
     * @param divider The divider.
     * @return <code>true</code> if {@code number} is a multiple of {@code divider},
     *         <code>false</code> either.
     */
    private boolean isMultipleOf(final int number, final int divider) {
        return number % divider == 0;
    }

}
